package jp.tkym.labs.store;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * This provider cache instance for BeanstoreService.
 * @author takayama
 */
public class BeanstoreServiceProvider {
	private static final BeanstoreServiceProvider singleton = new BeanstoreServiceProvider();
	private static final String DEFAULT_KEY = "DEFAULT_FACTORY";
	private Properties properties;
	private BeanstoreServiceTypedProvider current;
	private Map<String, BeanstoreServiceTypedProvider> providerMap;
	
	/**
	 * Default Factory class is Sqlite.
	 **/
	private BeanstoreServiceProvider(){
		InputStream is = BeanstoreServiceProvider.class.getResourceAsStream("beanstore.properties");
		properties = new Properties();
		try {
			properties.load(is);
		} catch (IOException e) {
		}
		providerMap = new ConcurrentHashMap<String, BeanstoreServiceTypedProvider>();
	}
	
	public BeanstoreService get(String arg) throws BeanstoreException{
		return current().get(arg);
	}
	
	BeanstoreServiceTypedProvider current() throws BeanstoreException{
		if(current == null){
			current = def(DEFAULT_KEY);
		}
		return current;
	}
	
	public <T extends BeanstoreServiceFactory> BeanstoreServiceTypedProvider def(String def) throws BeanstoreException {
		BeanstoreServiceTypedProvider typedProvider = providerMap.get(def);
		if(typedProvider == null){
			typedProvider = new BeanstoreServiceTypedProvider(DEFAULT_KEY);
			providerMap.put(DEFAULT_KEY, typedProvider);
		}
		current = typedProvider;
		return typedProvider;
	}
	
	public static BeanstoreServiceProvider getInstance(){
		return singleton;
	}

	public class BeanstoreServiceTypedProvider{
		private Map<String, BeanstoreService> servicemap;
		private BeanstoreServiceFactory factory;
		
		BeanstoreServiceTypedProvider(String factoryKey) throws BeanstoreException{
			String className = properties.getProperty(factoryKey);
			if(className == null) 
				throw new BeanstoreException(factoryKey+" is not exists on beanstore.properties");
			try {
				factory = createFactory(Class.forName(className));
			} catch (ClassNotFoundException e) {
				throw new BeanstoreException(e);
			}
			servicemap = new ConcurrentHashMap<String, BeanstoreService>();
		}
		
		BeanstoreServiceFactory createFactory(Class<?> cls) throws BeanstoreException{
			try {
				return (BeanstoreServiceFactory) cls.newInstance();
			} catch (InstantiationException e) {
				throw new BeanstoreException(e);
			} catch (IllegalAccessException e) {
				throw new BeanstoreException(e);
			}
		}
		
		public BeanstoreService get(String arg) throws BeanstoreException{
			BeanstoreService service = servicemap.get(arg);
			if(service == null){
				service = factory.createBeanstoreService(arg);
				servicemap.put(arg, service);
			}
			return service;
		}
	}
}